♞ Knight Movement Problems: BFS and DP

Solving Minimum Steps (BFS) and Probability (DP) problems.

Problem 1: Minimum Knight Steps (BFS)

Beginning from location (0,0) on an infinite board, what is the minimum number of steps needed for a knight to get to some other arbitrary location (x,y)?

Constraints: $1 \le x, y \le 8$

Why BFS?

This problem asks for the **minimum number of steps**, which immediately suggests a **Breadth-First Search (BFS)**.

Knight's Move Set (The 8 Possible Jumps)

A knight moves in an 'L' shape: 2 squares in one direction (horizontal or vertical) and 1 square in the perpendicular direction. From $(0, 0)$, the possible moves are:

(1, 2)
(-1, 2)
(1, -2)
(-1, -2)
(2, 1)
(-2, 1)
(2, -1)
(-2, -1)

The BFS Algorithm Steps

Data Structures Needed

  • **Queue:** Stores the positions to visit. Each element is `(x, y, steps)`.
  • **Visited Set/Map:** Stores all `(x, y)` locations already processed to prevent cycles and redundant work. This is crucial for an infinite board.

Detailed Steps

  1. **Initialization:** Add the starting position $\mathbf{(0, 0)}$ with $\mathbf{0}$ steps to the Queue and mark it as visited.
  2. **Loop:** While the Queue is not empty, dequeue the front position $\mathbf{(x_c, y_c, s_c)}$.
  3. **Check Goal:** If $\mathbf{(x_c, y_c)}$ is the target $\mathbf{(x, y)}$, return $\mathbf{s_c}$.
  4. **Generate Next Moves:** Iterate through the 8 possible Knight moves (dx, dy). Calculate the new position $\mathbf{(x_n, y_n)}$.
  5. **Enqueue and Mark:** If $\mathbf{(x_n, y_n)}$ has not been visited, mark it as visited and enqueue $\mathbf{(x_n, y_n, s_c + 1)}$.

Algorithm Complexity

For a general graph, Time is $O(V + E)$ (Vertices + Edges). On a chessboard, we can approximate the search space to be the area reachable within the minimum number of steps $M$.

Time: $O(M^2)$ (approximated search space)

Space: $O(M^2)$ (for the Visited Set and Queue)

Interactive BFS Demo (Start at 0, 0)

Search Status

Waiting for target coordinates...

BFS Queue (Next to Visit)

Queue is empty.

Code Snippet (BFS Main Loop)

function bfs() {
    // Queue stores {x, y, steps}
    while (queue.length > 0) {
        let {x, y, steps} = queue.shift();
        if (x === targetX && y === targetY) {
            return steps; // Found shortest path!
        }
        
        // Explore 8 moves
        for (let i = 0; i < 8; i++) {
            let nextX = x + DX[i];
            let nextY = y + DY[i];
            let key = `${nextX},${nextY}`;

            if (!visited.has(key)) {
                visited.add(key);
                queue.push({x: nextX, y: nextY, steps: steps + 1});
            }
        }
    }
}
                        

Problem 2: Knight Probability (Dynamic Programming)

Calculate the probability that a knight, starting at $(R, C)$ on an $N \times N$ board, remains on the board after exactly $K$ moves, where each move is random.

Constraints: $1 \le N \le 25, 0 \le K \le 100$.

The DP Approach

We use Dynamic Programming because the probability of the knight being at a certain square at step $k$ depends only on the probabilities of being at its neighboring squares at step $k-1$.

State Definition:

$DP[r][c]$ = Probability the knight is at $(r, c)$ after the current number of steps.

Transition:

The new probability $DP[r][c]$ is calculated by summing $\mathbf{1/8 \times \text{prevDP}[r'][c']}$ for all 8 positions $(r', c')$ from which the knight could jump to $(r, c)$ in one move.

Probability Calculator

Result

Enter parameters and click 'Calculate' to see the result.

Code Snippet (DP Core Logic)

function calculateKnightProbability(n, k, startR, startC) {
    if (k === 0) { return 1.0; }
    
    // DP table: probability of being at (r, c)
    let prevDp = Array(n).fill(0).map(() => Array(n).fill(0));
    
    // Base case: 100% probability at start position
    if (startR >= 0 && startR < n && startC >= 0 && startC < n) {
        prevDp[startR][startC] = 1.0;
    } else {
        return 0.0;
    }
    
    // Knight's moves (same as before)
    const DX = [1, 1, 2, 2, -1, -1, -2, -2];
    const DY = [2, -2, 1, -1, 2, -2, 1, -1];

    for (let s = 1; s <= k; s++) {
        let currentDp = Array(n).fill(0).map(() => Array(n).fill(0));
        
        for (let r = 0; r < n; r++) {
            for (let c = 0; c < n; c++) {
                // Check all 8 possible previous positions (r_prev, c_prev)
                for (let i = 0; i < 8; i++) {
                    const prevR = r - DX[i];
                    const prevC = c - DY[i];
                    
                    // Check if the previous position was on the board
                    if (prevR >= 0 && prevR < n && prevC >= 0 && prevC < n) {
                        // The probability of reaching (r, c) is the sum of probabilities 
                        // from all valid previous spots, multiplied by the move chance (1/8)
                        currentDp[r][c] += prevDp[prevR][prevC] * 0.125;
                    }
                }
            }
        }
        prevDp = currentDp; // Move forward one step
    }

    // Sum all probabilities in the final DP table
    let totalProbability = 0;
    for (let r = 0; r < n; r++) {
        for (let c = 0; c < n; c++) {
            totalProbability += prevDp[r][c];
        }
    }

    return totalProbability;
}